package org.cyberdyne.jabbertalky;

import java.util.Date;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Button;
import android.widget.Toast;

import org.cyberdyne.jabbertalky.SettingsActivity;
import org.cyberdyne.jabbertalky.R;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.muc.*;

public class jabbertalky extends Activity
{
	private String jabserver = "";
	private int jabport = 5222;
	private String ircChan = "";
	private String ircServ = "";
	private String ircNick = "";
	private String username = "";
	private String password = "";

	private TextView msgTextView;
	private Button btnSend;
	private EditText msgChat;
	private MultiUserChat muc = null;
	private XMPPConnection connection = null;
	private String chatMessage = "";
	private String strTitle = "";
	private Handler mHandler = new Handler();
	private SharedPreferences settings;

	/* Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		msgTextView = (TextView) findViewById(R.id.txtIRC);
		msgTextView.setMovementMethod(new ScrollingMovementMethod());
		msgTextView.setText(msgTextView.getText(), TextView.BufferType.EDITABLE);

		btnSend = (Button) findViewById(R.id.btnSend);
		msgChat = (EditText) findViewById(R.id.txtChatMsg);
	
		/* Create keyListener for our chat input box */
		msgChat.setOnKeyListener(new View.OnKeyListener()
		{
			public boolean onKey(View v, int keyCode, KeyEvent event)
			{
				return onViewKey(v, keyCode, event);
			}
		});

		/* Create send button onClick listener */
		btnSend.setOnClickListener(new View.OnClickListener()
		{
			public void onClick(View v)
			{
				sendChat(msgChat.getText().toString());
			}
		});
	} // END onCreate


	/**
	 * 
	 * onStart
	 * 
	 * Called when the activity is becoming visible to the user.
	 * Followed by onResume() if the activity comes to the foreground,
	 * or onStop() if it becomes hidden.
	 * 
	 */
	@Override
	public void onStart()
	{
		super.onStart();
		Log.v("XMPPClient", "Main:onStart");
		loadSettings();
	} // END onStart


	@Override
	public void onResume()
	{
		super.onResume();
		Log.v("XMPPClient", "Main:onResume");
		loadSettings();
	}


	@Override
	public void onPause()
	{
		super.onPause();
		Log.v("XMPPClient", "Main:onPause");
	}


	@Override
	public void onStop()
	{
		super.onStop();
		Log.v("XMPPClient", "Main:onStop");
	}


	@Override
	protected void onRestoreInstanceState(Bundle state)
	{
		super.onRestoreInstanceState(state);
		Log.v("XMPPClient", "Main:onRestoreInstanceState");
	}


	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);
		Log.v("XMPPClient", "Main:onSaveInstanceState");
	}


	/**
	 * dialogMsg
	 * 
	 * @param msg
	 * 
	 */
	public void dialogMsg(String msg)
	{
		Toast toast = Toast.makeText(this, msg, Toast.LENGTH_LONG);
		View textView = toast.getView();
		LinearLayout lay = new LinearLayout(this);
		lay.setOrientation(LinearLayout.HORIZONTAL);
		lay.addView(textView);
		toast.setDuration(4000);
		toast.setView(lay);
		toast.show(); 
	} // END dialogMsg
	
	
	/**
	 * 
	 * makeJabberConnection
	 * 
	 * Creates a connection to a jabber server.
	 * 
	 * @param strHost
	 * @param port
	 * @return
	 */
	public boolean makeJabberConnection(String strHost, int port)
	{
		SmackConfiguration.setPacketReplyTimeout(6000);
		ConnectionConfiguration connConfig = new ConnectionConfiguration(strHost, port);
		connConfig.setReconnectionAllowed(true);
		connection = new XMPPConnection(connConfig);

		try
		{
			Log.i("XMPPClient", "Connecting to " + strHost + ":" + port);
			dialogMsg("Connecting to " + strHost + ":" + port);
			connection.connect();
			Log.i("XMPPClient", "Connected to " + connection.getHost());
			dialogMsg("Connected to " + connection.getHost());

			try
			{
				Log.i("XMPPClient", "Logging in as " + username + "/" + password);
				dialogMsg("Logging in as " + username + "/" + password);
				connection.login(username, password);
				Log.i("XMPPClient", "Logged in!");
				dialogMsg("Logged in!");
				Chatter(connection);
				return true;
			}
			catch ( XMPPException ex )
			{
				Log.e("XMPPClient", "Failed to log in as " + username);
				dialogMsg(":( " + ex.getMessage().toString());
			}

		}
		catch ( XMPPException ex )
		{
			Log.e("XMPPClient", "Failed to connect to " + connection.getHost() + ":" + connection.getPort());
			dialogMsg(":( " + ex.getMessage().toString());
		}

		return false;

	} // END makeJabberConnection


	/**
	 * 
	 * Chatter
	 * 
	 * Joins the channel and applies Listeners
	 * 
	 * @param conn
	 */
	public void Chatter(XMPPConnection conn)
	{
		Log.i("XMPPClient", "Creating MUC");
		dialogMsg("creating " + ircChan + "%" + ircServ + "@irc." + jabserver);
		muc = new MultiUserChat(conn, ircChan + "%" + ircServ + "@irc." + jabserver);

		addMessageListener();
		addSubjectUpdatedListener();
		addPresenceListener();
		// addParticipantListener();
		addUserStatusListener();

		try
		{
			Log.i("XMPPClient", "Attempting to join: " + muc.getRoom() + " as " + ircNick);
			dialogMsg("Attempting to join: " + muc.getRoom() + " as " + ircNick);
			muc.join(ircNick);			
		}
		catch ( XMPPException ex )
		{
			Log.e("XMPPClient", "Join Error: " + ex.toString() + " " + muc.getRoom());
			dialogMsg("Join Error: " + ex.toString() + " " + muc.getRoom());
		}

		Log.v("XMPPClient", "Joined!!!");
		dialogMsg("\\o/");

		String channel = new String();
		String[] temp = null;

		temp = muc.getRoom().toString().split("%");
		channel = temp[0];
		this.setTitle("#" + channel + ": ");
	}


	/**
	 * 
	 * onViewKey
	 * 
	 * Key Handler for chat input box.
	 * 
	 * @param v
	 * @param keyCode
	 * @param event
	 * @return
	 */
	public boolean onViewKey(View v, int keyCode, KeyEvent event)
	{
		switch ( keyCode )
		{
			// If 'ENTER' send their message.
			case KeyEvent.KEYCODE_ENTER:
				sendChat(msgChat.getText().toString());
				break;
		}
		return false;
	}// END onViewKey


	/**
	 * 
	 * sendChat
	 * 
	 * Sends our text to the jabber server
	 * 
	 * @param muc
	 * @param str
	 */
	private void sendChat(String str)
	{
		if ( str.trim().length() > 0 )
		{

			if ( str.startsWith("/") )
			{
				String[] cmd;

				cmd = str.split(" ");

				try
				{
					Log.i("XMPPClient", "Cmd: " + cmd[0]);
					if ( cmd[0].toLowerCase().equals("/topic") )
					{
						Log.i("XMPPClient", "/TOPIC");
						muc.changeSubject("New topic");
					}
					else if ( cmd[0].toLowerCase() == "/op" )
					{
						Log.i("XMPPClient", "/OP");
						muc.grantModerator("iCEBrkr");
					}
					else if ( cmd[0].toLowerCase() == "/join" )
					{
						Log.i("XMPPClient", "/JOIN");
						muc = new MultiUserChat(connection, ircChan + "%" + ircServ + "@irc." + jabserver);
						muc.join(ircNick);
					}
					else if ( cmd[0].toLowerCase() == "/leave" )
					{
						Log.i("XMPPClient", "/LEAVE");
						muc.leave();
					}
					else if ( cmd[0].toLowerCase().equals("/kick") )
					{
						Log.i("XMPPClient", "/KICK");
						muc.kickParticipant("iCEBrkr", "yerrrrrrr outta here!");
					}
				}
				catch ( XMPPException ex )
				{
					Log.e("XMPPClient", "Command Error: " + ex.toString());
				}
			}
			else
			{
				try
				{
					muc.sendMessage(str.trim());
				}
				catch ( XMPPException ex )
				{
					Log.e("XMPPClient", "Send Message Error: " + ex.toString());
				}
			}

			msgChat.setText("");
			msgChat.focusSearch(View.FOCUS_LEFT);
		}
	} // END sendChat


	/**
	 * 
	 * addMessageListener
	 * 
	 * Listen for group chat messages
	 * 
	 * @param muc
	 */
	private void addMessageListener()
	{
		Log.i("XMPPClient", "Adding MessageListener");
		PacketListener messageListener = new PacketListener()
		{
			public void processPacket(Packet packet)
			{
				String msgFrom = new String();
				String[] temp = null;
				Message jabbmessage = (Message) packet;
				jabbmessage.setProperty("receiveTime", new Long(new Date().getTime()));
				Log.v("XMPPClient", "Chat: (" + jabbmessage.getType() + ") " + jabbmessage.getFrom() + ": " + jabbmessage.getBody());
				try
				{
					if ( jabbmessage.getType() == Message.Type.groupchat )
					{
						if ( jabbmessage.getFrom().contains("/") )
						{
							temp = jabbmessage.getFrom().split("/");
							msgFrom = temp[1];

							if ( jabbmessage.getBody().startsWith("/me") )
							{
								chatMessage = "* " + msgFrom + " " + jabbmessage.getBody().replace("/me", "");
							}
							else
							{
								chatMessage = "<" + msgFrom + "> " + jabbmessage.getBody();
							}

							mHandler.post(new Runnable()
							{
								public void run()
								{
									addText();
								}
							});
						}
					}
					else if ( jabbmessage.getType() == Message.Type.chat )
					{
						Log.i("XMPPClient", "Private Message: " + jabbmessage.getBody());
						dialogMsg(jabbmessage.getBody().toString());
					}
				}
				catch ( Exception e )
				{
					Log.v("XMPPClient", "processPacket error: " + e.toString());
					Log.v("XMPPClient", "Message: " + jabbmessage.getBody().toString());
				}
			}
		};
		muc.addMessageListener(messageListener);
	}// END addMessageListener


	/**
	 * 
	 * addSubjectUpdatedListener
	 * 
	 * Listens for /TOPIC changes.
	 * 
	 * @param muc
	 */
	private void addSubjectUpdatedListener()
	{
		Log.i("XMPPClient", "Adding Subject listener");
		SubjectUpdatedListener subjectUpatelistener = new SubjectUpdatedListener()
		{
			@Override
			public void subjectUpdated(String strTopic, String strWho)
			{
				String msgFrom = new String();
				String channel = new String();
				String[] temp = null;

				Log.i("XMPPClient", "Topic Change: " + strTopic + " :: " + strWho);

				temp = muc.getRoom().toString().split("%");
				channel = temp[0];

				if ( strWho.contains("/") )
				{
					Log.i("XMPPClient", "Found who set it");

					temp = strWho.split("/");
					msgFrom = temp[1];
					// chatMessage = "::: Topic changed by " + msgFrom +
					// " on " + channel + ": " + strTopic;
					// chatMessage = "::: Topic changed by " + msgFrom +
					// ": " + strTopic;

					mHandler.post(new Runnable()
					{
						public void run()
						{
							// addText();
						}
					});
				}
				else
				{
					setTitle("#" + muc.getRoom() + ": " + strTopic);
				}
			}
		};
		muc.addSubjectUpdatedListener(subjectUpatelistener);
	}


	/**
	 * 
	 * addPresenseListener
	 * 
	 * @param muc
	 */
	private void addPresenceListener()
	{
		Log.i("XMPPClient", "Adding PresenceListener");
		PacketFilter filter = new PacketTypeFilter(Presence.class);
		connection.createPacketCollector(filter);

		PacketListener myListener = new PacketListener()
		{
			@Override
			public void processPacket(Packet packet)
			{
				Presence presence = (Presence) packet;
				Log.i("XMPPClient", "Packet: " + packet.toString());
				Log.i("XMPPClient", "Presence: " + presence.toString());
				Log.i("XMPPClient", "Presence:1 " + presence.getFrom());
				Log.i("XMPPClient", "Presence:2 " + presence.getStatus().toString());
				Log.i("XMPPClient", "Presence:3 " + presence.getType().toString());

				String[] tmp;
				String nickname = new String();
				String channel = new String();

				tmp = presence.getFrom().split("/");
				nickname = tmp[1];

				// ** Someone joined.
				if ( presence.getType() == Presence.Type.available )
				{
					tmp = presence.getFrom().split("%");
					channel = tmp[0];

					// ** ::: Join: iCEBrkrX has joined #ib
					chatMessage = "::: Join: " + nickname + " has joined #" + channel;
					mHandler.post(new Runnable()
					{
						public void run()
						{
							addText();
						}
					});
				}

				// ** Someone left!
				if ( presence.getType() == Presence.Type.unavailable )
				{
					tmp = presence.getFrom().split(" ");
					channel = tmp[2];

					// ** ::: Part: iCEBrkr has left #ib ()
					chatMessage = "::: Part: " + nickname + " has left " + channel;
					mHandler.post(new Runnable()
					{
						public void run()
						{
							addText();
						}
					});
				}

			}
		};
		connection.addPacketListener(myListener, filter);
	}


	/**
	 * 
	 * addParticipantListener
	 * 
	 * 
	 * @param muc
	 */
	private void addParticipantListener()
	{
		Log.i("XMPPClient", "Adding addParticipantListener");
		PacketListener participantListener = new PacketListener()
		{
			@Override
			public void processPacket(Packet packet)
			{
				Message jabbmsg = (Message) packet;
				Log.v("XMPPClient", "Participant: " + jabbmsg.toString());
			}
		};
		muc.addParticipantListener(participantListener);
	}


	private void addUserStatusListener()
	{
		Log.i("XMPPClient", "Adding UserStatusListener");
		UserStatusListener userstatusListener = new UserStatusListener()
		{
			@Override
			public void adminGranted()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "+o");
			}


			@Override
			public void adminRevoked()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "-o");
			}


			@Override
			public void banned(String arg0, String arg1)
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "banned");
			}


			@Override
			public void kicked(String arg0, String arg1)
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "kicked");
			}


			@Override
			public void membershipGranted()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "membership granted");
			}


			@Override
			public void membershipRevoked()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "membership revoked");
			}


			@Override
			public void moderatorGranted()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "moderated added");
			}


			@Override
			public void moderatorRevoked()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "moderated revoked");
			}


			@Override
			public void ownershipGranted()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "ownership added");
			}


			@Override
			public void ownershipRevoked()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "ownership revoked");
			}


			@Override
			public void voiceGranted()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "voice added");
			}


			@Override
			public void voiceRevoked()
			{
				// TODO Auto-generated method stub
				Log.i("XMPPClient", "voice removed");
			}
		};
		muc.addUserStatusListener(userstatusListener);
	}


	/**
	 * 
	 * addText
	 * 
	 * Adds text to the chat-window.
	 * 
	 */
	private void addText()
	{
		Editable text = (Editable) msgTextView.getText();
		text.append(chatMessage + "\n");
		/*
		 * Log.d("XMPPClient", "ScrollY: " + msgTextView.getScrollY());
		 * int off = mLayout.getMeasuredHeight() -
		 * msgTextView.getHeight(); if ( off > 0 ) {
		 * msgTextView.scrollTo(0, off); }
		 */
	} // END addText


	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		super.onCreateOptionsMenu(menu);

		menu.add(0, 0, 0, "Settings");
		menu.add(0, 1, 1, "Connect");
		menu.add(0, 2, 2, "About");
		return true;
	}


	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch ( item.getItemId() )
		{
			case 0:
				Intent iSettings = new Intent(this, SettingsActivity.class);
				startActivity(iSettings);
				return true;
			case 1:
				loadSettings();

				if ( connection == null )
				{
					if ( !makeJabberConnection(jabserver, jabport) )
					{
						Log.e("XMPPClient", "Unable to make connection");
						dialogMsg(":( " + "Unable to make connection");
						connection = null;
					}
				}
				return true;
				
		}
		return super.onOptionsItemSelected(item);
	}


	public void loadSettings()
	{
		settings = getSharedPreferences("JTSettings", Context.MODE_PRIVATE);
		
		jabserver = settings.getString("jabServer", "192.168.1.2").trim();
		jabport = settings.getInt("jabPort", 5222);
		username = settings.getString("jabUsername","username").trim();
		password = settings.getString("jabPassword","password").trim();
		ircServ = settings.getString("ircServer","radon.slashnet.org").trim();
		ircNick = settings.getString("ircNickname", "JabberTalky").trim();
		ircChan = settings.getString("ircChannel", "iB").trim();
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)
	{
		if ( keyCode == KeyEvent.KEYCODE_BACK )
		{
			Log.v("XMPPClient","Disconnecting...");
			connection.disconnect();
			this.finish();
		}
		return super.onKeyDown(keyCode, event);
	}
	
}